#ifndef _TASK_DAG_GRAPH_
#define _TASK_DAG_GRAPH_
#ifdef __cplusplus
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <tr1/unordered_map>
#include <tr1/unordered_set>
#include <vector>

#include "pthread.h"

// #include "aceMesh_task.h"
#include "aceMesh_runtime.h"
#include "dag_graph_check.h"
#ifdef USE_COMPOSITE_TASK
#include "affinity_id_generator_range.h"
#else
#include "affinity_id_generator.h"
#endif

#ifdef AUTO_PARTITION
#include "disjoin_set.h"
#endif

#ifdef ACEMESH_PERFORMANCE
#include "aceMesh_performance.h"
#endif

#ifdef DEBUG_GRAPH
#endif

extern "C" void add_addr(aceMesh_task *self, void *addr, int area_type, int rw_type,
                         int is_neighbor);
extern "C" void wait_for_all(task *self);
extern "C" void aceMesh_task_constructor(struct aceMesh_task *self);
extern "C" void aceMesh_task_destructor(aceMesh_task *p_acemesh_task);
extern "C" void init_spawn(task *t);
extern "C" void increment_ref_count(task *self);

extern "C" void add_end_successor(aceMesh_task *self, struct task *t);
extern "C" void set_affinity_id(task *self, int id);
extern "C" struct empty_task *empty_task_constructor();
extern "C" void empty_task_destructor(struct empty_task *p_empty_task);
extern "C" void store_info(aceMesh_task *self);
extern "C" void spawn_to_id(struct task *t);
extern "C" int get_loop_id(struct aceMesh_task *self);
extern "C" int get_task_id(struct aceMesh_task *self);

#ifdef CONCURRENT_CONSTRUCT_GRAPH
extern "C" int del_successor(aceMesh_task *self, aceMesh_task *t);

extern "C" void set_pre(struct task *self, int p);
extern "C" int pre_count(struct task *self);
extern "C" int ref_count(struct task *self);
extern "C" int add_successor(aceMesh_task *self, aceMesh_task *t);
extern "C" int set_vertical_task(aceMesh_task *self, aceMesh_task *t);
#else
extern "C" void add_successor(aceMesh_task *self, aceMesh_task *t);
extern "C" int add_successor_first_ret(aceMesh_task *self, aceMesh_task *t);
extern "C" void set_vertical_task(aceMesh_task *self, aceMesh_task *t);
#endif
// extern "C" void free_all(aceMesh_task* self, std::tr1::unordered_set<task *> wait_free_tasks);
namespace AceMesh_runtime
{

// for mult DAG buffer
typedef struct dag_instance_type {
#ifdef DEBUG_GRAPH
    end_task *endTask;
#else
    empty_task *endTask;
#endif
    std::vector<aceMesh_task *> need_spawn_tasks;
    std::vector<aceMesh_task *> vector_tasks;
} dag_instance;

// for interface input
typedef struct tuple_addr_type {
    void *addr;
    int area_type;
    int type;
    int neighbor;
    int neighbor_type;
} addr_tuple;

// for recording input for one virtual task
typedef struct tuple_addr_task {
    void *addr;
    aceMesh_task *t;
    int type;
#if !defined(LIGHT_BUILD_GRAPH) && !defined(LIGHT_BUILD_GRAPH2)
    int significance;
#endif
} virtual_tuple;

// for hash map
typedef struct task_addr_relation {
    aceMesh_task *t;
#if !defined(LIGHT_BUILD_GRAPH) && !defined(LIGHT_BUILD_GRAPH2)
    int significance;  //
    task_addr_relation() : t(NULL), significance(0)
#else
    task_addr_relation() : t(NULL)
#endif

    {
    }
} task_significance;

// for hash_map
typedef struct tuple_rw_task {
    std::vector<task_significance> r_tasks;
    task_addr_relation w_task;
    tuple_rw_task() {}
} rw_task_tuple;

// for check confict
struct update_addr_flag {
    bool w_flag;
    bool r_flag;
    update_addr_flag() : w_flag(false), r_flag(false) {}
};

// bool run(bool is);
//
// for debug
#ifdef DEBUG_GRAPH
struct end_task {
    struct aceMesh_task aceMesh_task_base;
};
struct end_task *end_task_constructor();
#endif

#ifdef ACEMESH_PERFORMANCE
#include "aceMesh_performance.h"
#endif

// seq
class task_dag_graph
{
public:
    // tmp test var
    task_dag_graph();
    ~task_dag_graph();

    void init(int thread_num, char *my_dir);
    Error_Code register_task(aceMesh_task *t, std::vector<addr_tuple> &addrs);
    inline Error_Code register_access(aceMesh_task *t);
#ifdef ACEMESH_PROFILING_CLASSIFY
    Error_Code spawn_and_wait(int classify_id);
#else
    Error_Code spawn_and_wait();
#endif
    void free_tasks();

#ifdef SPECIFY_END_TASKS
    Error_Code acemesh_add_end_task(aceMesh_task *t);
#endif

    Error_Code end_split_task();
    Error_Code begin_split_task();
    Error_Code spawn_and_goout();
#ifdef REUSE_GRAPH
    Error_Code dag_start(int dagNo, int *int_vec, int n1, double *float_vec, int n2);
#endif
    void init_thread_num(int thread_num);
#ifdef AUTO_AFFINITY
    void reset_affinity_id();
#endif
    // for debug
#ifdef DEBUG_GRAPH
    Error_Code begin_split_task(const std::string &loop_info);
    Error_Code spawn_and_wait(int print_graph);
#endif
    Error_Code get_end_tasks();

#ifdef SUPPORT_PARTITION
    void set_separations(std::map<void *, int> &sep_datas);
    Error_Code spawn_and_wait_with_separation();
#endif

#ifdef AUTO_PARTITION
    Error_Code spawn_and_wait_with_separation();
#endif

    inline void get_neighbour_init();
    void push_neighbor_addr(void *addr);
#ifdef AUTO_AFFINITY
#ifdef USE_COMPOSITE_TASK
    affinity_id_generator_range my_affinity;
#else
    affinity_id_generator my_affinity;
#endif
#endif

#ifdef CONCURRENT_CONSTRUCT_GRAPH
    Error_Code wait_for_all_task();
#endif

private:
    Error_Code spawn();
    Error_Code wait();
    Error_Code end_construct_dag();
    void _save_to_buffer();
    void _resume_from_buffer();
    int curr_dagNo;
#ifdef DEBUG_GRAPH
    bool check_graph();
    bool check_graph(int print_graph);
    bool g_print_graph;
#endif

#ifdef AUTO_PARTITION
    void spawn(int sep_id);
    void end_construct_dag_separation();
#endif

#ifdef SUPPORT_PARTITION
    void end_construct_dag_separation();
#endif

#ifdef REUSE_GRAPH
    void _store_dag_info();
#endif

    inline void *get_true_addr(void *addr, int type);
    int update_write_addr(std::map<void *, update_addr_flag> &addr_update, void *addr, int type);
    inline int build_releationship(aceMesh_task *dest, int type, tuple_rw_task &src,
                                   bool is_neighbor);
    int thread_num;
    void add_tuples_to_virtual_task(aceMesh_task *t, void *addr, int type, int significance);
    void reset_task_graph();
    void destroy_task_graph();
    inline void do_analysis_addr();

    std::vector<aceMesh_task *> need_spawn_tasks;
    std::tr1::unordered_map<void *, tuple_rw_task> addr_task;

    std::vector<tuple_addr_task> one_virtual_task_tuples;

#ifdef LOW_COUPLING_GRAPH
    std::vector<tuple_addr_task> w_one_virtual_task_tuples;
    std::vector<tuple_addr_task> r_one_virtual_task_tuples;
    std::vector<std::pair<aceMesh_task *, aceMesh_task *> > dep_pair;
    void add_and_cluster_tuples_to_virtual_task(aceMesh_task *t, void *addr, int type,
                                                int significance);
    inline void collect_dep(aceMesh_task *pred_task, aceMesh_task *succ_task, int branch);
    inline void reset_virtual_data();
    void update_hashmap_and_collect_dep_according_virtual_task_tuple();
    void update_hashmap_and_collect_dep_according_virtual_task_tuple_with_clustering();
    void construct_dep_according_dep_pair();
    std::vector<aceMesh_task *> virtual_need_spawn_tasks;
#endif
    void unique_tuples();
    inline int type_add(int type1, int type2);
    inline int significance_add(int significance1, int significace2);

    // need to judge whether task is end
    std::tr1::unordered_set<aceMesh_task *> end_tasks;
    std::tr1::unordered_set<aceMesh_task *> end_tasks2;
    void add_end_task(aceMesh_task *t);
    void del_end_task(aceMesh_task *t);

    std::tr1::unordered_map<int, std::vector<double> > dag_fvec_map;
    std::tr1::unordered_map<int, std::vector<int> > dag_ivec_map;
    std::tr1::unordered_map<int, int> dag_reuse_cnt;
    std::tr1::unordered_set<task *> wait_free_tasks;
#ifdef REUSE_GRAPH
    std::tr1::unordered_map<int, bool> dag_reuse_flag;
    // dag buffer
    std::tr1::unordered_map<int, dag_instance> dag_instance_map;
#endif

    // reuse this var
    void *neighbor_addrs[MAX_NEIGHBORS];
    int num_neighbs;
    std::vector<aceMesh_task *> vector_tasks;
#ifndef DEBUG_GRAPH
    empty_task *endTask;
#else
    end_task *endTask;
#endif

    // for debug
#ifdef DEBUG_GRAPH
    dag_graph_check graph_check;
    bool check_graph_dfs();
    bool check_graph_bfs();
    int task_nums;
#endif

#ifdef AUTO_PARTITION
    int sep_id;
    disjoin_set my_disjoin_set;
    std::map<int, aceMesh_task *> sep_task;
    std::map<int, std::vector<int> > sep_data;
    std::map<int, task *> sep_end_tasks;
    void union_set(aceMesh_task *src, aceMesh_task *dest);
#endif

#ifdef SUPPORT_PARTITION
    std::map<int, std::vector<aceMesh_task *> > sep_data;
    std::map<int, task *> sep_end_tasks;
#endif

#ifdef ACEMESH_PERFORMANCE
    aceMesh_performance my_perf;
#endif
};
}  // namespace AceMesh_runtime
#endif
// for slave
Error_Code slave_register_child_task(aceMesh_task *t);
Error_Code slave_register_access(aceMesh_task *t);
Error_Code slave_spawn_and_wait();
Error_Code slave_spawn_and_goout();
#endif
